package com.bid;


import java.io.IOException;
import java.util.ArrayList;

import javax.microedition.khronos.opengles.GL10;

import org.anddev.andengine.engine.Engine;
import org.anddev.andengine.engine.camera.ZoomCamera;
import org.anddev.andengine.engine.camera.hud.HUD;
import org.anddev.andengine.engine.handler.IUpdateHandler;
import org.anddev.andengine.engine.options.EngineOptions;
import org.anddev.andengine.engine.options.EngineOptions.ScreenOrientation;
import org.anddev.andengine.engine.options.resolutionpolicy.RatioResolutionPolicy;
import org.anddev.andengine.entity.IEntity;
import org.anddev.andengine.entity.modifier.MoveModifier;
import org.anddev.andengine.entity.modifier.RotationModifier;
import org.anddev.andengine.entity.modifier.ScaleModifier;
import org.anddev.andengine.entity.scene.Scene;
import org.anddev.andengine.entity.scene.Scene.IOnSceneTouchListener;
import org.anddev.andengine.entity.scene.menu.MenuScene;
import org.anddev.andengine.entity.scene.menu.MenuScene.IOnMenuItemClickListener;
import org.anddev.andengine.entity.scene.menu.animator.AlphaMenuAnimator;
import org.anddev.andengine.entity.scene.menu.item.IMenuItem;
import org.anddev.andengine.entity.sprite.AnimatedSprite;
import org.anddev.andengine.entity.util.FPSLogger;
import org.anddev.andengine.extension.input.touch.controller.MultiTouch;
import org.anddev.andengine.extension.input.touch.controller.MultiTouchController;
import org.anddev.andengine.extension.input.touch.detector.PinchZoomDetector;
import org.anddev.andengine.extension.input.touch.detector.PinchZoomDetector.IPinchZoomDetectorListener;
import org.anddev.andengine.extension.input.touch.exception.MultiTouchException;
import org.anddev.andengine.extension.physics.box2d.FixedStepPhysicsWorld;
import org.anddev.andengine.extension.physics.box2d.PhysicsConnector;
import org.anddev.andengine.extension.physics.box2d.PhysicsFactory;
import org.anddev.andengine.input.touch.TouchEvent;
import org.anddev.andengine.input.touch.detector.ScrollDetector;
import org.anddev.andengine.input.touch.detector.ScrollDetector.IScrollDetectorListener;
import org.anddev.andengine.input.touch.detector.SurfaceScrollDetector;
import org.anddev.andengine.level.LevelLoader;
import org.anddev.andengine.level.LevelLoader.IEntityLoader;
import org.anddev.andengine.level.util.constants.LevelConstants;
import org.anddev.andengine.opengl.texture.TextureOptions;
import org.anddev.andengine.opengl.texture.atlas.bitmap.BitmapTextureAtlas;
import org.anddev.andengine.opengl.texture.atlas.bitmap.BitmapTextureAtlasTextureRegionFactory;
import org.anddev.andengine.opengl.texture.region.TextureRegion;
import org.anddev.andengine.opengl.texture.region.TiledTextureRegion;
import org.anddev.andengine.ui.activity.BaseGameActivity;
import org.anddev.andengine.util.Debug;
import org.anddev.andengine.util.SAXUtils;
import org.anddev.andengine.util.modifier.IModifier;
import org.anddev.andengine.util.modifier.IModifier.IModifierListener;
import org.anddev.andengine.util.modifier.ease.EaseExponentialIn;
import org.xml.sax.Attributes;

import android.hardware.SensorManager;
import android.view.KeyEvent;
import android.widget.Toast;

import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.Body;
import com.badlogic.gdx.physics.box2d.BodyDef.BodyType;
import com.badlogic.gdx.physics.box2d.Contact;
import com.badlogic.gdx.physics.box2d.ContactImpulse;
import com.badlogic.gdx.physics.box2d.ContactListener;
import com.badlogic.gdx.physics.box2d.Manifold;

/**
 * (c) 2010 Nicolas Gramlich
 * (c) 2011 Zynga
 *
 * @author Nicolas Gramlich
 * @since 18:47:08 - 19.03.2010
 */
public class GameCore extends BaseGameActivity implements IOnSceneTouchListener, IScrollDetectorListener, IPinchZoomDetectorListener, IOnMenuItemClickListener{
        // ===========================================================
        // Constants
        // ===========================================================
		private static final int MENU_RESTART = 0;
		private static final int MENU_QUIT = MENU_RESTART + 1;
      
        // ===========================================================
        // Fields
        // ===========================================================
        
        private ZoomCamera mCamera;
        private MenuScene mMenuScene;
        private SidePopUp sideMenu;
        private SurfaceScrollDetector mScrollDetector;
        private PinchZoomDetector mPinchZoomDetector;
        private float mPinchZoomStartedCameraZoomFactor;
        private boolean mIsZooming = false;
        
        private BitmapTextureAtlas mBackgroundTextureAtlas;	/*Texture*/
        private BitmapTextureAtlas mGameLevelsTextureAtlas;	/*Texture*/
        private BitmapTextureAtlas mMenuTextureAtlas;	/*Texture*/
        private BitmapTextureAtlas mSlideMenuTextureAtlas; /*Texture*/
        private TextureRegion mSlideMenuBackgroundTextureRegion; /*Slide menu background*/
        private TiledTextureRegion mSlideMenuChildren[] = new TiledTextureRegion[3]; /*Slide menu NEXT,RESTART,QUIT*/
        private TiledTextureRegion mMenuRestartTextureRegion;	/*Menu Restart*/
        private TiledTextureRegion mMenuQuitTextureRegion;	/*Menu Quit*/
        private TiledTextureRegion mBackgroundTextureRegion;	/*Background*/
        private TiledTextureRegion mBoxTextureRegion [] = new TiledTextureRegion[3];	/*Box*/
        private TiledTextureRegion mCircleTextureRegion [] = new TiledTextureRegion[3]; /*Circle*/
        private TiledTextureRegion mTriangleTextureRegion [] = new TiledTextureRegion[3]; /*Polygon*/
        private TiledTextureRegion msBoxTextureRegion [] = new TiledTextureRegion[3];	/*sBox*/
        private TiledTextureRegion msCircleTextureRegion [] = new TiledTextureRegion[3]; /*sCircle*/
        private TiledTextureRegion msTriangleTextureRegion [] = new TiledTextureRegion[3]; /*sPolygon*/
        
        private Scene mScene;
        private FixedStepPhysicsWorld mPhysicsWorld;
        
        private ArrayList <BoxObject> mSpriteList = new ArrayList();
        private int boxObjectsNumber;
        private static int boxObjectClicked;
        private int boxObjectsRemaining = 0;
        private int boxObjectsToDestroy = 0;
        private float SCENE_WIDTH;
        private float SCENE_HEIGHT;
        private float ratioMedium;
        
        public static boolean restartPending = false;
        public static boolean quitPending = false;
        public static boolean deleteAllPending = false;
        public static boolean newGamePending = false;
        // ===========================================================
        // Constructors
        // ===========================================================

        // ===========================================================
        // Getter & Setter
        // ===========================================================
        
        private void initialize(){
        	SCENE_WIDTH = 960;
        	SCENE_HEIGHT = 640;
        	ratioMedium = BID.ratioMedium;
        }
        
        // ===========================================================
        // Methods for/from SuperClass/Interfaces
        // ===========================================================

        @Override
        public Engine onLoadEngine() {
        		/*Initialization*/
        		initialize();
        		
        		/*Camera*/
        		this.mCamera = new ZoomCamera(0, 0, BID.CAMERA_WIDTH, BID.CAMERA_HEIGHT);
        		this.mCamera.setBoundsEnabled(true);
        		this.mCamera.setBounds(0, SCENE_WIDTH, 0, SCENE_HEIGHT);
        		this.mCamera.setCenter(SCENE_WIDTH / 2f, SCENE_HEIGHT / 2f);
        		
        		/*Build engine */
                final EngineOptions engineOptions = new EngineOptions(true, ScreenOrientation.LANDSCAPE, new RatioResolutionPolicy(BID.CAMERA_WIDTH, BID.CAMERA_HEIGHT), this.mCamera);
                engineOptions.getTouchOptions().setRunOnUpdateThread(true);
                final Engine engine = new Engine(engineOptions);
                
                try {
        			if(MultiTouch.isSupported(this)) {
        				engine.setTouchController(new MultiTouchController());
        			} else {
        				Toast.makeText(this, "Sorry your device does NOT support MultiTouch!\n\n(No PinchZoom is possible!)", Toast.LENGTH_LONG).show();
        			}
        		} catch (final MultiTouchException e) {
        			Toast.makeText(this, "Sorry your Android Version does NOT support MultiTouch!\n\n(No PinchZoom is possible!)", Toast.LENGTH_LONG).show();
        		}
        		
                return engine;
        }

        @Override
        public void onLoadResources() {
                /* Textures. */
                this.mBackgroundTextureAtlas = new BitmapTextureAtlas(512, 512, TextureOptions.BILINEAR_PREMULTIPLYALPHA);
                this.mGameLevelsTextureAtlas = new BitmapTextureAtlas(512, 1024, TextureOptions.BILINEAR_PREMULTIPLYALPHA);
                this.mMenuTextureAtlas = new BitmapTextureAtlas(256, 512, TextureOptions.BILINEAR_PREMULTIPLYALPHA);
                this.mSlideMenuTextureAtlas = new BitmapTextureAtlas(256, 256, TextureOptions.BILINEAR_PREMULTIPLYALPHA);
                BitmapTextureAtlasTextureRegionFactory.setAssetBasePath("gfx/");
                
                /* TextureRegions. */
                loadRessourcesFromLevel(LevelPicker.UnlockedLevel);

        		this.mMenuRestartTextureRegion = BitmapTextureAtlasTextureRegionFactory.createTiledFromAsset(this.mMenuTextureAtlas, this, "menu_restart.png", 0, 0, 1, 1);//256x64*2
        		this.mMenuQuitTextureRegion = BitmapTextureAtlasTextureRegionFactory.createTiledFromAsset(this.mMenuTextureAtlas, this, "menu_quit.png", 0, 128, 1, 1);//256x64*2
        		
        		this.mSlideMenuBackgroundTextureRegion = BitmapTextureAtlasTextureRegionFactory.createFromAsset(this.mSlideMenuTextureAtlas, this, "slide_menu_background.png", 0, 0);//128x256
        		this.mSlideMenuChildren[0] = BitmapTextureAtlasTextureRegionFactory.createTiledFromAsset(this.mSlideMenuTextureAtlas, this, "slide_menu_next.png", 128, 0, 2, 1);//64*2x64
        		this.mSlideMenuChildren[1] = BitmapTextureAtlasTextureRegionFactory.createTiledFromAsset(this.mSlideMenuTextureAtlas, this, "slide_menu_restart.png", 128, 64, 2, 1);//64*2x64
        		this.mSlideMenuChildren[2] = BitmapTextureAtlasTextureRegionFactory.createTiledFromAsset(this.mSlideMenuTextureAtlas, this, "slide_menu_quit.png", 128, 128, 2, 1);//64*2x64
        		
                /* Loading Textures. */
                this.mEngine.getTextureManager().loadTexture(this.mBackgroundTextureAtlas);
                this.mEngine.getTextureManager().loadTexture(this.mGameLevelsTextureAtlas);
                this.mEngine.getTextureManager().loadTexture(this.mMenuTextureAtlas);
                this.mEngine.getTextureManager().loadTexture(this.mSlideMenuTextureAtlas);
        }

        @Override
        public Scene onLoadScene() {
                this.mEngine.registerUpdateHandler(new FPSLogger());
                this.mPhysicsWorld = new FixedStepPhysicsWorld(60, new Vector2(0, SensorManager.GRAVITY_EARTH), true);
                
                /*Contacts */
                manageContacts();
                
                /* Creating the scene. */
                this.mScene = new Scene();
                this.mScene.setOnSceneTouchListener(this);
                this.mScene.setOnSceneTouchListenerBindingEnabled(true);
                this.mScene.setOnAreaTouchTraversalFrontToBack();
                this.mScene.attachChild(new CustomBackground(0,0,SCENE_WIDTH, SCENE_HEIGHT, this.mBackgroundTextureRegion));
                
                this.createMenuScene();
        
                /*PinchZoom and Scrolling*/
                managePinchAndZoom();
               
                /* Make a virtual ground. */
                /*final Shape groundShape = new Rectangle(0,SCENE_HEIGHT - CONST.GROUND_HEIGHT,SCENE_WIDTH,CONST.GROUND_HEIGHT);
                final Body body = PhysicsFactory.createBoxBody(this.mPhysicsWorld, groundShape, BodyType.StaticBody,CONST.FIXTURE_WALL);
                body.setUserData(300);
                groundShape.setVisible(false);
                this.mScene.attachChild(groundShape);*/
                
                /*HUDS*/
                createHUDs();
                
                this.mScene.registerUpdateHandler(this.mPhysicsWorld);
              
                return this.mScene;
        }

        @Override
        public void onLoadComplete() {
        	GameCore.this.loadLevelFromXML(LevelPicker.levelSelected);
        }
        
        @Override
        public void onResumeGame() {
                super.onResumeGame();
                this.mEngine.start();
        }

        @Override
        public void onPauseGame() {
                super.onPauseGame();

                this.mEngine.stop();
        }
        @Override
        public boolean onSceneTouchEvent(final Scene pScene, final TouchEvent pSceneTouchEvent) {
        	/*Manage Zooming */
        	if(this.mPinchZoomDetector != null) {
    			this.mPinchZoomDetector.onTouchEvent(pSceneTouchEvent);

    			if(this.mPinchZoomDetector.isZooming()) {
    				this.mScrollDetector.setEnabled(false);
    			} else {
    				if(pSceneTouchEvent.isActionDown()) {
    					this.mScrollDetector.setEnabled(true);
    				}
    				this.mScrollDetector.onTouchEvent(pSceneTouchEvent);
    			}
    		} else {
    			this.mScrollDetector.onTouchEvent(pSceneTouchEvent);
    		}
        	
    		return true;
        }
        
        /* Scroll camera */
		@Override
		public void onScroll(ScrollDetector pScollDetector, TouchEvent pTouchEvent, float pDistanceX, float pDistanceY) {
			final float zoomFactor = this.mCamera.getZoomFactor();
			float incrementX = -pDistanceX / zoomFactor;
			float incrementY = -pDistanceY / zoomFactor;
			
			this.mCamera.offsetCenter(incrementX, incrementY);
			
		}

		@Override
		public void onPinchZoomStarted(PinchZoomDetector pPinchZoomDetector, TouchEvent pSceneTouchEvent) {
			this.mPinchZoomStartedCameraZoomFactor = this.mCamera.getZoomFactor();
			mIsZooming = true;
		}

		@Override
		public void onPinchZoom(PinchZoomDetector pPinchZoomDetector, TouchEvent pTouchEvent, float pZoomFactor) {
			this.mCamera.setZoomFactor(this.mPinchZoomStartedCameraZoomFactor * pZoomFactor);
			
		}
		
		@Override
		public void onPinchZoomFinished(PinchZoomDetector pPinchZoomDetector, TouchEvent pTouchEvent, float pZoomFactor) {
			this.mCamera.setZoomFactor(this.mPinchZoomStartedCameraZoomFactor * pZoomFactor);
			mIsZooming = false;
		}
		
        @Override
    	public boolean onKeyDown(final int pKeyCode, final KeyEvent pEvent) {
        	sideMenu.slideLeft();//Close slideMenu
        	
    		if((pKeyCode == KeyEvent.KEYCODE_BACK && this.mScene.hasChildScene() || pKeyCode == KeyEvent.KEYCODE_MENU) && pEvent.getAction() == KeyEvent.ACTION_DOWN) {
    			if(this.mScene.hasChildScene()) {
    				this.mScene.clearChildScene();
    			} else {
    				this.mScene.setChildScene(this.mMenuScene, false, true, true);
    			}
    			return true;
    		} else {
    			return super.onKeyDown(pKeyCode, pEvent);
    		}
    	}
        
        @Override
    	public boolean onMenuItemClicked(final MenuScene pMenuScene, final IMenuItem pMenuItem, final float pMenuItemLocalX, final float pMenuItemLocalY) {
    		switch(pMenuItem.getID()) {
    			case MENU_RESTART:
    				this.restartGame();
    				this.mScene.clearChildScene();
    				return true;
    			case MENU_QUIT:
    				this.finish();
    				return true;
    			default:
    				return false;
    		}
    	}
        
        // ===========================================================
        // Methods
        // ===========================================================
        
        private void loadRessourcesFromLevel(int currentLevel){
        	final int season = currentLevel / 9;
        	
        	final String levelSuffixDependent = "_" + season + "_" + currentLevel + ".png";
        	final String seasonSuffixDependent = "_" + season + ".png";
        	
        	/*Season dependent */
        	this.mBackgroundTextureRegion = BitmapTextureAtlasTextureRegionFactory.createTiledFromAsset(this.mBackgroundTextureAtlas, this, "background_tiled" + seasonSuffixDependent, 0, 0, 2, 1);//64*3 x 64
        	
        	this.mBoxTextureRegion[0] = BitmapTextureAtlasTextureRegionFactory.createTiledFromAsset(this.mGameLevelsTextureAtlas, this, "box_tiled" + seasonSuffixDependent, 0, 64, 2, 1);//64*3 x 64
        	this.mCircleTextureRegion[0] = BitmapTextureAtlasTextureRegionFactory.createTiledFromAsset(this.mGameLevelsTextureAtlas, this, "circle_tiled" + seasonSuffixDependent, 0, 128, 2, 1);//64*3 x 64
        	this.mTriangleTextureRegion[0] = BitmapTextureAtlasTextureRegionFactory.createTiledFromAsset(this.mGameLevelsTextureAtlas, this, "triangle_tiled" + seasonSuffixDependent, 0, 192, 2, 1);//64*3 x 64
        	this.mBoxTextureRegion[1] = BitmapTextureAtlasTextureRegionFactory.createTiledFromAsset(this.mGameLevelsTextureAtlas, this, "1_box_tiled" + seasonSuffixDependent, 0, 256, 2, 1);//64*3 x 64
        	this.mCircleTextureRegion[1] = BitmapTextureAtlasTextureRegionFactory.createTiledFromAsset(this.mGameLevelsTextureAtlas, this, "1_circle_tiled" + seasonSuffixDependent, 0, 320, 2, 1);//64*3 x 64
        	this.mTriangleTextureRegion[1] = BitmapTextureAtlasTextureRegionFactory.createTiledFromAsset(this.mGameLevelsTextureAtlas, this, "1_triangle_tiled" + seasonSuffixDependent, 0, 384, 2, 1);//64*3 x 64
        	this.mBoxTextureRegion[2] = BitmapTextureAtlasTextureRegionFactory.createTiledFromAsset(this.mGameLevelsTextureAtlas, this, "2_box_tiled" + seasonSuffixDependent, 0, 448, 2, 1);//64*3 x 64
        	this.mCircleTextureRegion[2] = BitmapTextureAtlasTextureRegionFactory.createTiledFromAsset(this.mGameLevelsTextureAtlas, this, "2_circle_tiled" + seasonSuffixDependent, 0, 512, 2, 1);//64*3 x 64
        	this.mTriangleTextureRegion[2] = BitmapTextureAtlasTextureRegionFactory.createTiledFromAsset(this.mGameLevelsTextureAtlas, this, "2_triangle_tiled" + seasonSuffixDependent, 0, 576, 2, 1);//64*3 x 64
        	
        	this.msBoxTextureRegion[0] = BitmapTextureAtlasTextureRegionFactory.createTiledFromAsset(this.mGameLevelsTextureAtlas, this, "s_box_tiled" + seasonSuffixDependent, 256, 64, 2, 1);//64*3 x 64
        	this.msCircleTextureRegion[0] = BitmapTextureAtlasTextureRegionFactory.createTiledFromAsset(this.mGameLevelsTextureAtlas, this, "s_circle_tiled" + seasonSuffixDependent, 256, 128, 2, 1);//64*3 x 64
        	this.msTriangleTextureRegion[0] = BitmapTextureAtlasTextureRegionFactory.createTiledFromAsset(this.mGameLevelsTextureAtlas, this, "s_triangle_tiled" + seasonSuffixDependent, 0, 192, 2, 1);//64*3 x 64
        	this.msBoxTextureRegion[1] = BitmapTextureAtlasTextureRegionFactory.createTiledFromAsset(this.mGameLevelsTextureAtlas, this, "1_s_box_tiled" + seasonSuffixDependent, 256, 256, 2, 1);//64*3 x 64
        	this.msCircleTextureRegion[1] = BitmapTextureAtlasTextureRegionFactory.createTiledFromAsset(this.mGameLevelsTextureAtlas, this, "1_s_circle_tiled" + seasonSuffixDependent, 256, 320, 2, 1);//64*3 x 64
        	this.msTriangleTextureRegion[1] = BitmapTextureAtlasTextureRegionFactory.createTiledFromAsset(this.mGameLevelsTextureAtlas, this, "1_s_triangle_tiled" + seasonSuffixDependent, 256, 384, 2, 1);//64*3 x 64
        	this.msBoxTextureRegion[2] = BitmapTextureAtlasTextureRegionFactory.createTiledFromAsset(this.mGameLevelsTextureAtlas, this, "2_s_box_tiled" + seasonSuffixDependent, 256, 448, 2, 1);//64*3 x 64
        	this.msCircleTextureRegion[2] = BitmapTextureAtlasTextureRegionFactory.createTiledFromAsset(this.mGameLevelsTextureAtlas, this, "2_s_circle_tiled" + seasonSuffixDependent, 256, 512, 2, 1);//64*3 x 64
        	this.msTriangleTextureRegion[2] = BitmapTextureAtlasTextureRegionFactory.createTiledFromAsset(this.mGameLevelsTextureAtlas, this, "2_s_triangle_tiled" + seasonSuffixDependent, 256, 576, 2, 1);//64*3 x 64
        	
        	
        	/*Season independent */
        	
        	/*Level dependent*/
        }
        
        private void loadLevelFromXML(int currentLevel){
        	final LevelLoader levelLoader = new LevelLoader();
    		levelLoader.setAssetBasePath("level/");

    		levelLoader.registerEntityLoader(LevelConstants.TAG_LEVEL, new IEntityLoader() {
    			@Override
    			public void onLoadEntity(final String pEntityName, final Attributes pAttributes) {
    				final int width = SAXUtils.getIntAttributeOrThrow(pAttributes, LevelConstants.TAG_LEVEL_ATTRIBUTE_WIDTH);
    				final int height = SAXUtils.getIntAttributeOrThrow(pAttributes, LevelConstants.TAG_LEVEL_ATTRIBUTE_HEIGHT);
    				boxObjectsNumber = SAXUtils.getIntAttributeOrThrow(pAttributes, CONST.LEVEL_TAG_ENTITY_NUMBER);
    				}
    		});

    		levelLoader.registerEntityLoader(CONST.TAG_ENTITY, new IEntityLoader() {
    			@Override
    			public void onLoadEntity(final String pEntityName, final Attributes pAttributes) {
    				final int x = SAXUtils.getIntAttributeOrThrow(pAttributes, CONST.TAG_ENTITY_ATTRIBUTE_X);
    				final int y = SAXUtils.getIntAttributeOrThrow(pAttributes, CONST.TAG_ENTITY_ATTRIBUTE_Y);
    				final int width = SAXUtils.getIntAttributeOrThrow(pAttributes, CONST.TAG_ENTITY_ATTRIBUTE_WIDTH);
    				final int height = SAXUtils.getIntAttributeOrThrow(pAttributes, CONST.TAG_ENTITY_ATTRIBUTE_HEIGHT);
    				final int type = SAXUtils.getIntAttributeOrThrow(pAttributes, CONST.TAG_ENTITY_ATTRIBUTE_TYPE);
    				final int code = SAXUtils.getIntAttributeOrThrow(pAttributes, CONST.TAG_ENTITY_ATTRIBUTE_CODE);
    				final float rotation = SAXUtils.getFloatAttributeOrThrow(pAttributes, CONST.TAG_ENTITY_ATTRIBUTE_ROTATION_ANGLE);
    				
    				GameCore.this.addSpriteToScreen(x, y, width, height, type, code, rotation);
    			}
    		});

    		try {
    			levelLoader.loadLevelFromAsset(this, "level_" + currentLevel + ".lvl");
    		} catch (final IOException e) {
    			Debug.e(e);
    		}
        }
        
        private void addSpriteToScreen(final float pX, final float pY, final float width, final float height, final int type,final int code, float rotationAngle) {
    		BoxObject sprite = null;
            Body body = null;
            rotationAngle = rotationAngle * (float)Math.PI / 180f;
            if(code > 0)
            	this.boxObjectsRemaining = ++ this.boxObjectsToDestroy;
  
    		switch(type){
            case CONST.DYNAMIC_BOX : /*Dynamic Box Sprite */
            	sprite = new BoxObject(pX, pY, width, height, this.mBoxTextureRegion[code].clone(), type, code, rotationAngle);
            	body = PhysicsFactory.createBoxBody(this.mPhysicsWorld, sprite, BodyType.DynamicBody,CONST.FIXTURE_NORMAL);
            	break;
            case CONST.DYNAMIC_CIRCLE : /*Dynamic Circle Sprite */
            	sprite = new BoxObject(pX, pY, width, height, this.mCircleTextureRegion[code].clone(), type, code, rotationAngle);
            	body = PhysicsFactory.createCircleBody(this.mPhysicsWorld, sprite, BodyType.DynamicBody,CONST.FIXTURE_NORMAL);
            	break;
            case CONST.DYNAMIC_TRIANGLE : /*Dynamic Triangle Sprite */
            	{
            		sprite = new BoxObject(pX, pY, width, height, this.mTriangleTextureRegion[code].clone(), type, code, rotationAngle);
            	
                	final float halfWidth = width / 2f / CONST.PTM;
                	final float halfHeight = height / 2f / CONST.PTM;
                	
                	final float top = -halfHeight;
                	final float down = +halfHeight;
                	final float midX = 0f;
                	final float left = -halfWidth;
                	final float right = +halfWidth;
            	
                	final Vector2 vertices[] = {
                			new Vector2(midX, top),
                			new Vector2(right, down),
                			new Vector2(left, down)
                	};
                	body = PhysicsFactory.createPolygonBody(this.mPhysicsWorld, sprite, vertices, BodyType.DynamicBody,CONST.FIXTURE_NORMAL);
                }
            	break;
            
            case CONST.FIXED_BOX : /*Fixed Box Sprite*/
            	sprite = new BoxObject(pX, pY, width, height, this.msBoxTextureRegion[code].clone(), type, code, rotationAngle);
            	body = PhysicsFactory.createBoxBody(this.mPhysicsWorld, sprite, BodyType.StaticBody,CONST.FIXTURE_WALL);
            	break;
            case CONST.FIXED_CIRCLE : /*Fixed Circle Sprite */
            	sprite = new BoxObject(pX, pY, width, height, this.msCircleTextureRegion[code].clone(), type, code, rotationAngle);
            	body = PhysicsFactory.createCircleBody(this.mPhysicsWorld, sprite, BodyType.StaticBody,CONST.FIXTURE_WALL);
            	break;
            case CONST.FIXED_TRIANGLE : /*Fixed Triangle Sprite */
            	{
            		sprite = new BoxObject(pX, pY, width, height, this.msTriangleTextureRegion[code].clone(), type, code, rotationAngle);
            	
                	final float halfWidth = width / 2f / CONST.PTM;
                	final float halfHeight = height / 2f / CONST.PTM;
                	
                	final float top = -halfHeight;
                	final float down = +halfHeight;
                	final float midX = 0f;
                	final float left = -halfWidth;
                	final float right = +halfWidth;
            	
                	final Vector2 vertices[] = {
                			new Vector2(midX, top),
                			new Vector2(right, down),
                			new Vector2(left, down)
                	};
                	body = PhysicsFactory.createPolygonBody(this.mPhysicsWorld, sprite, vertices, BodyType.StaticBody,CONST.FIXTURE_NORMAL);
            	}
                break;
            	
            default :
            	
            	break;
    		}
    		
            mPhysicsWorld.registerPhysicsConnector(new PhysicsConnector(sprite, body, true, true));
            sprite.setUserData(body);
            body.setUserData(this.mSpriteList.size());
            body.setActive(false);
            body.setTransform(pX + width / 2f, pY + height / 2f, rotationAngle);
            
            this.mSpriteList.add(sprite);	sprite.setNumber(this.mSpriteList.size() - 1);
            
            this.mScene.attachChild(sprite);
            this.mScene.registerTouchArea(sprite);
            
            if(this.mSpriteList.size() == this.boxObjectsNumber){
            	for(int i = 0; i < this.boxObjectsNumber; i ++){
            		final Body bd = ((Body)this.mSpriteList.get(i).getUserData());
            		bd.setActive(true);
            		bd.setAwake(true);
            	}
            }
            
        }
        
        private void restartGame(){
        	this.mCamera.setCenter(SCENE_WIDTH / 2f, SCENE_HEIGHT / 2f);
        	this.mCamera.setCenter(SCENE_WIDTH / 2f, SCENE_HEIGHT / 2f);
        	
        	for(int i = 0; i < this.mSpriteList.size(); i ++){
        		this.mSpriteList.get(i).addSpriteToScreen();
        	}
        	this.boxObjectsRemaining = this.boxObjectsToDestroy;
        	
        	this.mCamera.setZoomFactor(1f);
        }
        
        private void deleteAllSprites(){
        	for(int i =this.mSpriteList.size() - 1; i >= 0; i --){
        		final BoxObject box = this.mSpriteList.get(i);
        		final PhysicsConnector facePhysicsConnector = this.mPhysicsWorld.getPhysicsConnectorManager().findPhysicsConnectorByShape(box);
	
	            this.mPhysicsWorld.unregisterPhysicsConnector(facePhysicsConnector);
	            this.mPhysicsWorld.destroyBody(facePhysicsConnector.getBody());
	
	            this.mScene.unregisterTouchArea(box);
	            this.mScene.detachChild(box);
	            
	            this.mSpriteList.remove(i);
        	}
        	
        	this.boxObjectsToDestroy = 0;
        	
	        System.gc();
        }
        protected void createMenuScene() {
    		this.mMenuScene = new MenuScene(this.mCamera);
    		this.mMenuScene.setMenuAnimator(new AlphaMenuAnimator(10f)); // **********
    		
    		final MenuItem resetMenuItem = new MenuItem(MENU_RESTART, this.mMenuRestartTextureRegion);
    		resetMenuItem.setSize(BID.CAMERA_WIDTH / 2, BID.CAMERA_HEIGHT / 4f);
    		resetMenuItem.setBlendFunction(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);
    		this.mMenuScene.addMenuItem(resetMenuItem);

    		final MenuItem quitMenuItem = new MenuItem(MENU_QUIT, this.mMenuQuitTextureRegion);
    		quitMenuItem.setSize(BID.CAMERA_WIDTH / 2, BID.CAMERA_HEIGHT / 4f);
    		quitMenuItem.setBlendFunction(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);
    		this.mMenuScene.addMenuItem(quitMenuItem);
    		
    		this.mMenuScene.buildAnimations();

    		this.mMenuScene.setBackgroundEnabled(false);

    		this.mMenuScene.setOnMenuItemClickListener(this);
    	}
        
        private void manageContacts(){
        	 this.mPhysicsWorld.setContactListener(new ContactListener(){
					@Override
					public void beginContact(Contact contact) {
						final Integer nrA = (Integer)contact.getFixtureA().getBody().getUserData();
						final Integer nrB = (Integer)contact.getFixtureB().getBody().getUserData();
						if(nrA == null || nrB == null || nrA.intValue() >= GameCore.this.mSpriteList.size() || nrB.intValue() >= GameCore.this.mSpriteList.size()){
							
						}else{
							final int codeA = GameCore.this.mSpriteList.get(nrA.intValue()).getCode();
							final int codeB = GameCore.this.mSpriteList.get(nrB.intValue()).getCode();
							final int typeA = GameCore.this.mSpriteList.get(nrA.intValue()).getType();
							final int typeB = GameCore.this.mSpriteList.get(nrB.intValue()).getType();
							if(codeA == codeB && codeA != 0 ){
								GameCore.this.mSpriteList.get(nrA.intValue()).removePending = true;
								GameCore.this.mSpriteList.get(nrB.intValue()).removePending = true;
							}
						}
					}
					@Override
					public void endContact(Contact contact) {
					}
					@Override
					public void preSolve(Contact contact, Manifold oldManifold) {
					}
					@Override
					public void postSolve(Contact contact, ContactImpulse impulse) {
					}
             	
             });
        }
        
        private void managePinchAndZoom(){
        	this.mScrollDetector = new SurfaceScrollDetector(this);
            if(MultiTouch.isSupportedByAndroidVersion()) {
    			try {
    				this.mPinchZoomDetector = new PinchZoomDetector(this);
    			} catch (final MultiTouchException e) {
    				this.mPinchZoomDetector = null;
    			}
    		} else {
    			this.mPinchZoomDetector = null;
    		}
            this.mScene.registerUpdateHandler(new IUpdateHandler(){
            	float zoomStepUp = 0.05f;
            	float zoomStepDown = 0.05f;
            	float actualZoom = 1;
            	float zoomError = 0.10f;
				@Override
				public void onUpdate(float pSecondsElapsed) {
					if(!mIsZooming){
						actualZoom = GameCore.this.mCamera.getZoomFactor();
						if(actualZoom < CONST.MIN_ZOOM - zoomError){
							GameCore.this.mCamera.setZoomFactor(actualZoom + zoomStepUp * actualZoom);
						}
						if(actualZoom > CONST.MAX_ZOOM + zoomError){
							GameCore.this.mCamera.setZoomFactor(actualZoom - zoomStepDown * actualZoom);
						}
					}
					
					if(restartPending){
						restartPending = false;
						restartGame();
					}
					if(quitPending){
						quitPending = false;
						GameCore.this.finish();
					}
					if(deleteAllPending){
						deleteAllPending = false;
						deleteAllSprites();
					}
					if(newGamePending){
						newGamePending = false;
						loadLevelFromXML(LevelPicker.levelSelected);
					}
				}
				@Override
				public void reset() {
				}
            });
        }
        
        private void createHUDs(){
        	HUD hud = new HUD();
        	sideMenu = new SidePopUp(-130, 112, 128, 256, this.mSlideMenuBackgroundTextureRegion, this.mSlideMenuChildren);
        	sideMenu.setHUD(hud);
        	sideMenu.createItems();
        	sideMenu.slideLeft();
        	hud.attachChild(sideMenu);
        	this.mCamera.setHUD(hud);
        }
        // ===========================================================
        // Inner and Anonymous Classes
        // ===========================================================
		
        /*Box Object class*/
		private class BoxObject extends AnimatedSprite {
			private boolean isOnStage = true;
			private boolean isDown = false;
			private boolean removePending = false;
			
			private int number;
			private int type;
			private int code;
			private float rotation;
			
			private float defaultX;
			private float defaultY;
			
			public BoxObject(float pX, float pY, float pTileWidth, float pTileHeight, TiledTextureRegion pTiledTextureRegion, int pType, int pCode, float pRotation) {
				super(pX, pY, pTileWidth, pTileHeight, pTiledTextureRegion);
				
				this.setType(pType);	this.setCode(pCode);	this.rotation = pRotation;
				
				this.defaultX = pX;	this.defaultY = pY;
				
				this.animate(200);
			}
	
			
			public void removeSpriteFromScreen(){
				this.stopAnimation();
				final Body body = ((Body)this.getUserData());
	        	body.setActive(false);
				if(this.code == 0){
		        	this.setVisible(false);
		        	this.isOnStage = false;
				}
				else{
					this.clearEntityModifiers();
					MoveModifier alpha = new MoveModifier(1f, this.getX(), (float)Math.random() * 960, this.getY(), -10, EaseExponentialIn.getInstance());
					alpha.addModifierListener(new IModifierListener <IEntity> (){
						@Override
						public void onModifierStarted(IModifier<IEntity> pModifier, IEntity pItem) {
						
						}
						@Override
						public void onModifierFinished(IModifier<IEntity> pModifier, IEntity pItem) {
							BoxObject.this.setVisible(false);
							BoxObject.this.isOnStage = false;
							BoxObject.this.setAlpha(1f);
							BoxObject.this.setScale(1f);
						}
						
					});
					this.registerEntityModifier(alpha);
					this.registerEntityModifier(new RotationModifier(1f, this.getRotation(), (float)Math.random() * 360));
					this.registerEntityModifier(new ScaleModifier(1f, 1f, 0f));
				}
	        }
			
			public void addSpriteToScreen(){
				this.setCurrentTileIndex(0);
				this.animate(200);
				
				final Body body = (Body)this.getUserData();
				body.setTransform((this.defaultX + this.getWidth() / 2f) / CONST.PTM, (this.defaultY + this.getHeight() / 2f) / CONST.PTM, this.rotation);
				body.setLinearVelocity(0f, 0f);
				body.setAngularVelocity(0f);
				body.setAwake(true);
				body.setActive(true);
				
				if(this.isOnStage == false)
					this.setVisible(true);
				
				this.isDown = false;
				this.isOnStage = true;
			}
			
			@Override
			protected void onManagedUpdate(final float pSecondsElapsed) {
				super.onManagedUpdate(pSecondsElapsed);
				if(this.removePending){
					GameCore.this.runOnUpdateThread(new Runnable(){
						@Override
						public void run() {
							BoxObject.this.removeSpriteFromScreen();
							BoxObject.this.removePending = false;
							GameCore.this.boxObjectsRemaining --;
							
							if(GameCore.this.boxObjectsRemaining == 0 ){
								LevelPicker.unlockPending = LevelPicker.levelSelected + 1;
								GameCore.this.sideMenu.slideRight();
							}
						}
					});
				}
				
				/*Out Of Bounds */
				if(this.getCode() > 0){
					if(this.getX() + this.getWidth() < 0 || this.getY() + this.getHeight()  < 0 || this.getX() > SCENE_WIDTH || this.getY()> SCENE_HEIGHT){
						GameCore.this.restartGame();
					}
				}
			}
			@Override
			public boolean onAreaTouched(final TouchEvent pSceneTouchEvent, final float pTouchAreaLocalX, final float pTouchAreaLocalY) {
				if(pSceneTouchEvent.isActionDown() && this.getCode() == 0){
					this.isDown = true;
					GameCore.boxObjectClicked = this.number;
				}
				if(pSceneTouchEvent.isActionUp() && this.isDown){
					if(GameCore.boxObjectClicked == this.number){
						this.removeSpriteFromScreen();
						this.isDown = false;
					}
				}
				
				GameCore.this.mScrollDetector.setEnabled(false);
				return true;
			}

			public void setNumber(final int number){
				this.number = number;
			}
			public void setType(int type) {
				this.type = type;
			}
			public int getType() {
				return this.type;
			}
			public void setCode(int code) {
				this.code = code;
			}
			public int getCode() {
				return this.code;
			}
		}
}